<?php
namespace classlib\database;
use classlib\orm;
class query {
    /**
     * 生成SQL语句的数组(包含table,field,value,where,orderby,groupby,is_replace,duplicate等)
     * @var array
     */
    protected $datas;
    
    /**
     * 数据库解析实例
     * @var parser
     */
    private $parser;
    
    /**
     * 构造函数
     * @param parser $parser 数据库连接
     */
    public function __construct($parser) {
    	$this->parser = $parser;
        $this->clear();
    }
    
    /**
     * 从查询器当中获取当前的解析引擎
     * @param  
     * @return parser
     * @static 
     */
    public function get_parser() {
    	return $this->parser;
    }
    
    /**
     * Hash @data 数据信息返回
     * @param mix $args
     * @return string
     */
    public function hash_data($args) {
    	$str = serialize($this->datas).serialize($args);
    	$str = md5($str);
    	return $str;
    }
    
    /**
     * 生成并执行SQL,然后返回结果数据集中的第一行记录的第一格数据
     * @param string $sql 直接执行原生SQL,如果是PDO模式,该参数类型为PDOStatement
     * @return string
     */
    public function get_value($sql = null) {
        if ($sql == null) {
            $sql = $this->as_sql('select', false);
        }
        $data = $this->parser->get_first($sql, orm::FETCH_NUM);
        return $data[0];
    }
    
    /**
     * 生成并执行SQL,然后返回一个数组对象表示结果数据集中的第一行记录
     * @param string $sql 直接执行原生SQL,如果是PDO模式,该参数类型为PDOStatement
     * @param int $fetchMode 记录提取模式 默认按字段名提取数据(orm::FETCH_ASSOC)
     * @return array
     */
    public function get_row($sql = null, $fetch_mode = orm::FETCH_ASSOC) {
        if ($sql == null) {
            $sql = $this->as_sql('select', false);
        }
        return $this->parser->get_first($sql, $fetch_mode);
    }
    
    /**
     * 生成并执行SQL,然后返回一个数组表示结果数据集中的第一列数据
     * @param string $sql 直接执行原生SQL,如果是PDO模式,该参数类型为PDOStatement
     * @param int $offset 偏移量
     * @param int $count 返回结果集记录条数
     * @return array
     */
    public function get_column($sql = null, $offset=0, $count=-1) {
        $result = array();
        if ($sql == null) {
        	$sql = $this->as_sql('select', false);
        }
        $list = $this->parser->get_all($sql, $offset, $count, orm::FETCH_NUM);
        foreach ($list as $item) {
            $result[] = $item[0];
        }
        return $result;
    }
    
    /**
     * 生成并执行SQL,然后返回一个数组表示一个数据集
     * @param string $sql 直接执行原生SQL,如果是PDO模式,该参数类型为PDOStatement
     * @param int $offset 偏移量
     * @param int $count 返回结果集记录条数
     * @param int $fetchMode 记录提取模式 默认字段名(orm::FETCH_ASSOC)
     * @return array
     */
    public function get_list($sql = null, $offset=0, $count=-1, $fetch_mode=orm::FETCH_ASSOC) {
        if ($sql == null) {
            $sql = $this->as_sql('select', false);
        }
        return $this->parser->get_all($sql, $offset, $count, $fetch_mode);
    }
    
    /**
     * 更新数据库记录,返回受影响的记录数
     * @param array $fields 内容为name=>字段值 或者 array('value'=>字段值,'type'=>字段类型)
     * @return int
     */
    public function update($fields = array()) {
        foreach ($fields as $field => $value) {
			if (!is_array($value)) {
				$value = array('value'=>$value, 'type'=>orm::DT_AUTO);
			} else if (is_numeric($field) && isset($value['name'])) {
				$field = $value['name'];
			}
			$this->datas['value'][$field] = $value;
		}
        return $this->parser->execute($this->as_sql('update', false));
    }
    
    /**
     * 插入数据库新记录,返回受影响的记录数
     * @param array $fields 内容为 字段名=>字段值 或者 array('value'=>字段值,'type'=>字段类型)
     * @return int
     */
    public function insert($fields = array(), $is_replace = false) {
        if (!empty($fields) && is_array($fields)) {
            foreach ($fields as $field => $value) {
                if (!is_array($value)) {
					$value = array('value'=>$value, 'type'=>orm::DT_AUTO);
				}
				$this->datas['value'][$field] = $value;
            }
        }
        $this->datas['is_replace'] = $is_replace;
        return $this->parser->execute($this->as_sql('insert', false));
    }
    
    /**
     * 设置插入主键冲突的时候使用更新字段信息
     * @param array $fields
     * @return $this
     */
    public function duplicate($fields = array()) {
    	if (!empty($fields) && is_array($fields)) {
    		foreach ($fields as $field => $value) {
    			if (!is_array($value)) {
    				$value = array('value'=>$value, 'type'=>orm::DT_AUTO);
    			}
    			$this->datas['duplicate'][$field] = $value;
    		}
    	}
    	return $this;
    }
    
    /**
     * 更新数据库记录,返回受影响的记录数,如果执行失败返回false
     * @return int
     */
    public function delete() {
        return $this->parser->execute($this->as_sql('delete', false));
    }
    
    /**
     * 返回生成的SQL脚本
     * @param string $type SQL查询语句的类型(select|update|delete|insert)
     * @param boolean $isForPrint 是否只是为了输出SQL语句(在PDO模式下才有意义)
     * @return string 返回生成的SQL脚本,如果是PDO模式且$isForPrint=false,则返回PDOStatement对象
     */
    public function as_sql($type = 'select', $isprint = true) {
        $this->datas['mode'] = $type;
        if((defined('OPENDEBUG') && OPENDEBUG) || $isprint) {
        	$sql = $this->parser->parse_sql($this->datas, true);
        	echo $sql;
        }
        return $this->parser->parse_sql($this->datas, false);
    }
    
    /**
     * 清空表,字段,约束
     * @param string $part 要清空的部分,默认清空全部,值可为:'table', 'field', 'value', 'where', 'group', 'order'
     * @return query
     */
    public function clear($part = null) {
        if (isset($this->datas[$part])) {
            $this->datas[$part] = array();
        } elseif ($part == null) {
            $this->datas = array ('mode' => 'select');
        }
        return $this;
    }
    
    /**
     * 加入表
     * @param string $table 表名
     * @param string $alias 别名
     * @param string $on 如果指定该参数，则加入的表使用left join,并以该参数指定的表达式为条件，第一次执行addTable方法时，该参数被忽略
     * @return query
     */
    public function table($table, $alias = '', $on = '') {
        $this->datas['table'][] = array('name'=>$table,
        	'alias'=>empty($alias)? false:$alias, 'on'=>(empty($on))? false:$on);
        return $this;
    }
    
    /**
     * 加入字段
     * @param string $fieldName 字段名
     * @param string $alias 别名
     * @param string $opt 操作/函数类型
     * @param string $fieldType 字段类型
     * @return query
     */
    public function field($field, $alias = '', $opt = '', $type=orm::DT_AUTO) {
        $this->datas['field'][] = array('name'=>$field, 
        	'alias'=>empty($alias)? '':$alias,'opt'=>$opt,'type'=>$type);
        return $this;
    }
    
    /**
     * 加入值
     * @param string $fieldName 字段名
     * @param string $fieldValue 字段值
     * @param string $fieldType 字段类型
     * @return query
     */
    public function value($field, $value, $type = orm::DT_AUTO) {
        $this->datas['value'][$field] = array('value'=>$value, 'type'=>$type);
        return $this;
    }
    
    /**
     * 加入约束条件
     * @param string $field 字段名
     * @param mixed $value 字段值, 默认值为NULL
     * @param string $opt 操作符, 默认值为orm::OP_EQ, 如果没有后续参数, 该参数可以表示$logical
     * @param string $type 字段类型, 默认值为orm::DT_AUTO, 如果没有后续参数, 该参数可以表示$logical
     * @param string $logical 逻辑关系操作符(orm::OP_AND|orm::OP_OR|orm::OP_NOT), 默认值为orm::OP_AND
     * @return query
     */
    public function where($field, $value=null, $opt=orm::OP_EQ, $type=orm::DT_AUTO, $logical=orm::OP_AND) {
        $num   = func_num_args();
        $where = array('name' => $field, 'value' => $value);
        if ($num == 3 && ($opt == orm::OP_AND || $opt == orm::OP_OR || $opt == orm::OP_NOT)) {
            $where['opt']     = orm::OP_EQ;
            $where['type']    = $type;
            $where['logical'] = $opt;
        } elseif ($num == 4 && ($type == orm::OP_AND || $type == orm::OP_OR || $type == orm::OP_NOT)) {
            $where['opt']     = $opt;
            $where['type']    = orm::DT_AUTO;
            $where['logical'] = $type;
        } else {
            $where['opt']     = $opt;
            $where['type']    = $type;
            $where['logical'] = $logical;
        }
        $this->datas['where'][] = $where;
        return $this;
    }
    
    /**
     * 加入分组
     * @param string $field
     * @return string
     * @return query
     */
    public function groupby($field) {
        $this->datas['group'][] = $field;
        return $this;
    }
    
    /**
     * 加入排序
     * @param string $field 字段名
     * @param string $orderby 排序方式
     * @return query
     */
    public function orderby($field, $orderby = orm::ASC) {
        $this->datas['order'][] = array('name'=>$field,'order'=>$orderby);
        return $this;
    }
}